/**
 * @file wk2xxx_spi.c
 * @author  xiaowine (xiaowine@sina.cn)
 * @brief
 * @version 01.00
 * @date    2022-03-25
 *
 * @copyright Copyright (c) {2020}  xiaowine
 *
 * @par 修改日志:
 * <table>
 * <tr><th>Date       <th>Version <th>Author  <th>Description
 * <tr><td>2022-03-25 <td>1.0     <td>wangh     <td>内容
 * </table>
 * ******************************************************************
 * *                   .::::
 * *                 .::::::::
 * *                ::::::::::
 * *             ..:::::::::::
 * *          '::::::::::::
 * *            .:::::::::
 * *       '::::::::::::::..        女神助攻,流量冲天
 * *            ..::::::::::::.     永不宕机,代码无bug
 * *          ``:::::::::::::::
 * *           ::::``:::::::::'        .:::
 * *          ::::'   ':::::'       .::::::::
 * *        .::::'      ::::     .:::::::'::::
 * *       .:::'       :::::  .:::::::::' ':::::
 * *      .::'        :::::.:::::::::'      ':::::
 * *     .::'         ::::::::::::::'         ``::::
 * * ...:::           ::::::::::::'              ``::
 * *```` ':.          ':::::::::'                  ::::.
 * *                   '.:::::'                    ':'````.
 * ******************************************************************
 */

/* Private includes ----------------------------------------------------------*/
#include "exti_bsp.h"
#include "wk2xxx.h"
/* Private typedef -----------------------------------------------------------*/

/* Private define ------------------------------------------------------------*/

/* Public define ------------------------------------------------------------*/

/* Private macro -------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/
const u8  UTxINT[4]      = {WK2XXX_UT1INT, WK2XXX_UT2INT, WK2XXX_UT3INT, WK2XXX_UT4INT};
const u16 rs485DirPin[4] = {GPIO_Pin_6, GPIO_Pin_5, GPIO_Pin_7, GPIO_Pin_3};
/* Public variables ---------------------------------------------------------*/

/* Private function prototypes -----------------------------------------------*/
static void    WK_SPI_Init(void);
static void    WK_RstInit(void);
static void    WK_485DirInit(void);
static void    Wk_Init(u8 port);
static uint8_t Wk_SetBaud(u8 port, uint32_t baudrate);
static void    WkWriteGReg(unsigned char greg, unsigned char dat);
static u8      WkReadGReg(unsigned char greg);
static void    WkWriteSReg(u8 port, u8 sreg, u8 dat);
static u8      WkReadSReg(u8 port, u8 sreg);
int            wk_RxChars(u8 port, u8 *recbuf);
static int     WK_RxFIFO(u8 port, u8 *recbuf);
static void    WkWriteSFifo(u8 port, u8 *dat, int num);
static void    WkReadSFifo(u8 port, u8 *rec, int num);
/* Private user code ---------------------------------------------------------*/

void wk2xxxInit(void)
{
    unsigned char dat1;
    WK_SPI_Init();  //初始化SPI总线
    /*读写GNEA，测试主接口通信是否成功*/
    dat1 = WkReadGReg(WK2XXX_GENA);
    rt_kprintf("gena=0x%x.\n", dat1);

    WK_RstInit();    /*硬件复位芯片*/
    WK_485DirInit(); /*485*/
    /*初始化子串口*/
    Wk_Init(1);
    Wk_Init(2);
    Wk_Init(3);
    Wk_Init(4);
    /*设置子串口波特率*/
    Wk_SetBaud(1, 2400);
    Wk_SetBaud(2, 115200);
    Wk_SetBaud(3, 2400);
    Wk_SetBaud(4, 9600);
    /*使能485*/
    // WK_RS485(1);
    // WK_RS485(2);
    // WK_RS485(3);
    // WK_RS485(4);
    EXTIX_Init();  //初始化CPU外部中断
}

static void WK_SPI_Init(void)
{
    // SPI_CS_Init();
    SPI_BUS_Init();
}

/**************************************WK_RstInit***********************************/
//函数功能:wk芯片需要用MCU的GPIO去控制RST引脚，本函数通过stm32的PB.8引脚连接WK的RST引脚
//初始化STM32的PB8引脚。
//
//*************************************************************************/
static void WK_RstInit(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //使能PB.7端口时钟
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_7;            // PB.7 端口配置
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;      //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      // IO口速度为50MHz
    GPIO_Init(GPIOB, &GPIO_InitStructure);                 //根据设定参数初始化GPIOB.7
    GPIO_SetBits(GPIOB, GPIO_Pin_7);                       // PB.7 输出高
    rt_thread_delay(50);
    GPIO_ResetBits(GPIOB, GPIO_Pin_7);  // PB.7 输出低
    rt_thread_delay(10);
    GPIO_SetBits(GPIOB, GPIO_Pin_7);  // PB.7 输出高
    rt_thread_delay(10);
}

/**************************************WK_485DirInit***********************************/
//函数功能:wk芯片需要用MCU的GPIO去控制RST引脚，本函数通过stm32的PD引脚连接485的引脚
//初始化STM32的PD3 5 6 7引脚。
//
//*************************************************************************/
static void WK_485DirInit(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);                               //使能PD端口时钟
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_3 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;  // PD 端口配置
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;                                   //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                                   // IO口速度为50MHz
    GPIO_Init(GPIOD, &GPIO_InitStructure);                                              //根据设定参数初始化GPIOD
    GPIO_SetBits(GPIOD, GPIO_Pin_3 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7);             // PD 输出高
}

/***************************WkWriteGReg***********************************/
//函数功能：写全局寄存器函数（前提是该寄存器可写，
//某些寄存器如果你写1，可能会自动置1，具体见数据手册)
//参数：
//      greg:为全局寄存器的地址
//      dat:为写入寄存器的数据
//***********************************************************************/
static void WkWriteGReg(unsigned char greg, unsigned char dat)
{
    u8 cmd;
    cmd = 0 | greg;
    SPI_CS_L();                     //拉低cs信号
    SPIx_ReadWriteByte(SPI2, cmd);  //写指令，对于指令的构成见数据手册
    SPIx_ReadWriteByte(SPI2, dat);  //写数据
    SPI_CS_H();                     //拉高cs信号
}
/****************************WkReadGReg***********************************/
//函数功能：读全局寄存器
//参数：
//      greg:为全局寄存器的地址
//      rec:返回的寄存器值
//***********************************************************************/
static u8 WkReadGReg(unsigned char greg)
{
    u8 cmd, rec;
    cmd = 0x40 | greg;
    SPI_CS_L();                         //拉低cs信号
    SPIx_ReadWriteByte(SPI2, cmd);      //写指令，对于指令的构成见数据手册
    rec = SPIx_ReadWriteByte(SPI2, 0);  //写数据
    SPI_CS_H();                         //拉高cs信号
    return rec;
}
/**************************WkWriteSReg***********************************/
//函数功能:
//参数：port:为子串口
//      sreg:为子串口寄存器
//      dat:为写入寄存器的数据
//注意：在子串口被打通的情况下，向FDAT写入的数据会通过TX引脚输出
//**********************************************************************/
static void WkWriteSReg(u8 port, u8 sreg, u8 dat)
{
    u8 cmd;
    cmd = 0x0 | ((port - 1) << 4) | sreg;
    SPI_CS_L();                     //拉低cs信号
    SPIx_ReadWriteByte(SPI2, cmd);  //写指令，对于指令的构成见数据手册
    SPIx_ReadWriteByte(SPI2, dat);  //写数据
    SPI_CS_H();                     //拉高cs信号
}
/**************************WkReadSReg***********************************/
//函数功能：读子串口寄存器
//参数：port为子串口端口号
//      sreg:为子串口寄存器地址
//      rec:返回的寄存器值
//**********************************************************************/
static u8 WkReadSReg(u8 port, u8 sreg)
{
    u8 cmd, rec;
    cmd = 0x40 | ((port - 1) << 4) | sreg;
    SPI_CS_L();                         //拉低cs信号
    SPIx_ReadWriteByte(SPI2, cmd);      //写指令，对于指令的构成见数据手册
    rec = SPIx_ReadWriteByte(SPI2, 0);  //写数据
    SPI_CS_H();                         //拉高cs信号
    return rec;
}

/************************WkWriteSFifo***********************************/
//函数功能:向子串口fifo写入需要发送的数据
//参数：port:为子串口
//      *dat:写入数据
//      num：为写入数据的个数，单次不超过256
//注意：通过该方式写入的数据，被直接写入子串口的缓存FIFO，然后被发送
//*********************************************************************/
static void WkWriteSFifo(u8 port, u8 *dat, int num)
{
    u8  cmd;
    int i;
    cmd = 0x80 | ((port - 1) << 4);
    if (num > 0) {
        SPI_CS_L();                     //拉低cs信号
        SPIx_ReadWriteByte(SPI2, cmd);  //写指令,对于指令构成见数据手册
        for (i = 0; i < num; i++) {
            SPIx_ReadWriteByte(SPI2, *(dat + i));  //写数据
        }
        SPI_CS_H();  //拉高cs信号
    }
}

/************************WkReadSFifo***********************************/
//函数功能:从子串口的fifo中读出接收到的数据
//参数：port:为子串口
//      *rec：接收到的数据
//      num：读出的数据个数。
//注意：通过该方式读出子串口缓存中的数据。单次不能超过256
//*********************************************************************/
static void WkReadSFifo(u8 port, u8 *rec, int num)
{
    u8  cmd;
    int n;
    cmd = 0xc0 | ((port - 1) << 4);
    if (num > 0) {
        SPI_CS_L();  //拉低cs信号
        SPIx_ReadWriteByte(SPI2, cmd);
        for (n = 0; n < num; n++) {
            *(rec + n) = SPIx_ReadWriteByte(SPI2, 0);
        }
        SPI_CS_H();  //拉高cs信号
    }
}

/*******WkInit*******************************************/
//函数功能：初始化子串口
/*******************************************************/
static void Wk_Init(u8 port)
{
    u8 gena, grst, gier, sier, scr;
    //使能子串口时钟
    gena = WkReadGReg(WK2XXX_GENA);
    gena = gena | (1 << (port - 1));
    WkWriteGReg(WK2XXX_GENA, gena);
    //软件复位子串口
    grst = WkReadGReg(WK2XXX_GRST);
    grst = grst | (1 << (port - 1));
    WkWriteGReg(WK2XXX_GRST, grst);
    //使能串口总中断
    gier = WkReadGReg(WK2XXX_GIER);
    gier = gier | (1 << (port - 1));
    WkWriteGReg(WK2XXX_GIER, gier);
    //使能子串口接收触点中断和超时中断
    sier = WkReadSReg(port, WK2XXX_SIER);
    sier |= WK2XXX_RFTRIG_IEN | WK2XXX_RXOUT_IEN | WK2XXX_TFEMPTY_IEN;
    WkWriteSReg(port, WK2XXX_SIER, sier);
    //初始化FIFO和设置固定中断触点
    WkWriteSReg(port, WK2XXX_FCR, 0XFF);
    //设置任意中断触点，如果下面的设置有效，
    //那么上面FCR寄存器中断的固定中断触点将失效
    WkWriteSReg(port, WK2XXX_SPAGE, 1);    //切换到page1
    WkWriteSReg(port, WK2XXX_RFTL, 0X64);  //设置接收触点为100个字节
    WkWriteSReg(port, WK2XXX_TFTL, 0X10);  //设置发送触点为16个字节
    WkWriteSReg(port, WK2XXX_SPAGE, 0);    //切换到page0
    //使能子串口的发送和接收使能
    scr = WkReadSReg(port, WK2XXX_SCR);
    scr |= WK2XXX_TXEN | WK2XXX_RXEN;
    WkWriteSReg(port, WK2XXX_SCR, scr);
}

/**************************Wk_SetBaud*******************************************************/
//函数功能：设置子串口波特率函数、此函数中波特率的匹配值是根据11.0592Mhz下的外部晶振计算的
// port:子串口号
// baud:波特率大小.波特率表示方式，
/**************************Wk2114SetBaud*******************************************************/
static uint8_t Wk_SetBaud(u8 port, uint32_t baudrate)
{
    uint32_t temp, freq;
    uint8_t  scr;
    uint8_t  baud1, baud0, pres;
    freq = 12000000; /*芯片外部时钟频率*/
                     //	freq=32000000;/*芯片外部时钟频率*/
    if (freq >= (baudrate * 16)) {
        temp  = (freq) / (baudrate * 16);
        temp  = temp - 1;
        baud1 = (uint8_t)((temp >> 8) & 0xff);
        baud0 = (uint8_t)(temp & 0xff);
        temp  = (((freq % (baudrate * 16)) * 100) / (baudrate));
        pres  = (temp + 100 / 2) / 100;
        rt_kprintf("Wk_SetBaud---freq:%d,baudrate:%d\n", freq, baudrate);
        rt_kprintf("Wk_SetBaud---baud1:%x,baud0:%x,pres:%x\n", baud1, baud0, pres);
        //关掉子串口收发使能
        scr = WkReadSReg(port, WK2XXX_SCR);
        WkWriteSReg(port, WK2XXX_SCR, 0);
        //设置波特率相关寄存器
        WkWriteSReg(port, WK2XXX_SPAGE, 1);  //切换到page1
        WkWriteSReg(port, WK2XXX_BAUD1, baud1);
        WkWriteSReg(port, WK2XXX_BAUD0, baud0);
        WkWriteSReg(port, WK2XXX_PRES, pres);
        WkWriteSReg(port, WK2XXX_SPAGE, 0);  //切换到page0
        //使能子串口收发使能
        WkWriteSReg(port, WK2XXX_SCR, scr);
        return 0;
    } else {
        rt_kprintf("Wk_SetBaud error！！！！\n");
        return 1;
    }
}
/**************************WK_TxChars*******************************************/
//函数功能:通过子串口发送固定长度数据
// port:端口号
// len:单次发送长度不超过256
//
/**************************WK_TxChars********************************************/
int wk_TxChars(u8 port, int len, u8 *sendbuf)
{
#ifdef RS485_USING
    if (len) {
        switch (port) {
            case 1:
                PORT1_485DIR_TX;
                break;
            case 2:
                PORT2_485DIR_TX;
                break;
            case 3:
                PORT3_485DIR_TX;
                break;
            case 4:
                PORT4_485DIR_TX;
                break;
            default:
                break;
        }
    }
#endif
#if 1
    WkWriteSFifo(port, sendbuf, len);  //通过fifo方式发送数据
#else
    int num = len;
    for (num = 0; num < len; num++) {
        WkWriteSReg(port, WK2XXX_FDAT, *(sendbuf + num));
    }
#endif
    return len;
}
/**************************WK_RxChars*******************************************/
//函数功能:读取子串口fifo中的数据
// port:端口号
// recbuf:接收到的数据
// 返回值：接收数据的长度
/**************************WK_RxChars********************************************/
int wk_RxChars(u8 port, u8 *recbuf)
{
    u8  fsr = 0, rfcnt = 0, rfcnt2 = 0, sifr = 0;
    int len = 0;
    sifr    = WkReadSReg(port, WK2XXX_SIFR);

    if ((sifr & WK2XXX_RFTRIG_INT) || (sifr & WK2XXX_RXOVT_INT))  //有接收中断和接收超时中断
    {
        fsr    = WkReadSReg(port, WK2XXX_FSR);
        rfcnt  = WkReadSReg(port, WK2XXX_RFCNT);
        rfcnt2 = WkReadSReg(port, WK2XXX_RFCNT);
        // printf("rfcnt=0x%x.\n",rfcnt);
        /*判断fifo中数据个数*/
        if (fsr & WK2XXX_RDAT) {
            if (!(rfcnt2 >= rfcnt)) {
                rfcnt = rfcnt2;
            }
            len = (rfcnt == 0) ? 256 : rfcnt;
        }
#if 1
        WkReadSFifo(port, recbuf, len);
#else
        for (int n = 0; n < len; n++)
            *(recbuf + n) = WkReadSReg(port, WK2XXX_FDAT);
#endif
        return len;
    } else {
        len = 0;
        return len;
    }
}
/**************************WK_RxFIFO*******************************************/
//函数功能:读取子串口fifo中的数据
// port:端口号
// recbuf:接收到的数据
// 返回值：接收数据的长度
/**************************WK_RxChars********************************************/
static int WK_RxFIFO(u8 port, u8 *recbuf)
{
    u8  fsr = 0, rfcnt = 0, rfcnt2 = 0;
    int len = 0;

    fsr    = WkReadSReg(port, WK2XXX_FSR);
    rfcnt  = WkReadSReg(port, WK2XXX_RFCNT);
    rfcnt2 = WkReadSReg(port, WK2XXX_RFCNT);
    // printf("rfcnt=0x%x.\n",rfcnt);
    /*判断fifo中数据个数*/
    if (fsr & WK2XXX_RDAT) {
        if (!(rfcnt2 >= rfcnt)) {
            rfcnt = rfcnt2;
        }
        len = (rfcnt == 0) ? 256 : rfcnt;
    }
    WkReadSFifo(port, recbuf, len);
    return len;
}

uint8_t wkReadSerilData(uint8_t *port, uint8_t *buf, uint8_t *len)
{
    uint8_t gifr, sifr, i;
    *len = 0;
    gifr = WkReadGReg(WK2XXX_GIFR);
    /*数据处理*/
    for (i = 0; i < 4; i++) {
        if (gifr & UTxINT[i]) {  //判断子串口1是否有中断
            *port = i;
            gifr &= (~UTxINT[i]);
            sifr = WkReadSReg(i + 1, WK2XXX_SIFR);
            if (sifr & WK2XXX_TFEMPTY_INT) {
                if (i < 3) {
                    PORTx_485DIR_RX(i + 1);

                } else {
                    PORT4_485DIR_RX;
                }
            }
            if ((sifr & WK2XXX_RXOVT_INT) || (sifr & WK2XXX_RFTRIG_INT)) {
                *len = WK_RxFIFO(i + 1, buf);  //一次接收的数据不会超过256Byte
                return (gifr & 0x0f);
            }
        }
    }
    return (gifr & 0x0f);
}
